package com.simple.relaxed.ability;

import com.simple.relaxed.anno.AddRoute;
import com.simple.relaxed.anno.MainRoute;
import com.simple.relaxed.anno.OnKeyUp;
import com.simple.relaxed.anno.RegisterSlice;
import com.simple.relaxed.anno.impl.InjectImpl;
import com.simple.relaxed.anno.impl.RegisterSliceImpl;
import com.simple.relaxed.exception.AnnotationAbsenceException;
import com.simple.relaxed.manager.impl.AsyncTaskManager;
import com.simple.relaxed.manager.impl.SliceManager;
import com.simple.relaxed.utils.AsyncTaskUtil;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityConnection;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.multimodalinput.event.KeyEvent;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public abstract class PageBaseAbility extends Ability {

    /**
     * 表示任务在UI线程执行
     */
    public static final int UI_STACK = 0x001;

    /**
     * 表示任务在非UI线程执行
     */
    public static final int OTHER_STACK = 0x002;

    private Class<? extends Ability> aClass;

    private Map<Integer, Method> keyUpMethods;

    protected TaskDispatcher uiTaskDispatcher;

    public PageBaseAbility() {
        super();
        aClass = getClass();
        keyUpMethods = new HashMap<>();
    }

    @Override
    protected void onStart(Intent intent) {
        InjectImpl.inject(this);
        uiTaskDispatcher = getUITaskDispatcher();
        super.onStart(intent);
        MainRoute mainRoute = aClass.getDeclaredAnnotation(MainRoute.class);
        if (mainRoute == null) {
            throw new AnnotationAbsenceException("缺少注解：".concat(MainRoute.class.getName()));
        }
        super.setMainRoute(mainRoute.cls().getName());
        // 寻找键盘抬起事件
        initMethodToKeyUp();
        // 寻找被AddRoute注解标记的Method方法，并且触发它
        initAddRoute();
        // 注册路由
        RegisterSliceImpl.registerSliceImpl(this);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
        boolean state = false;
        Method method = keyUpMethods.get(keyCode);
        if (method == null) {
            return super.onKeyUp(keyCode, keyEvent);
        } else {
            method.setAccessible(true);
            try {
                Object invoke = method.invoke(this);
                if (invoke instanceof Boolean) {
                    state = (boolean) invoke;
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return state;
    }

    /**
     * 启动任务，通过bindName找到任务方法，并在指定的类型的线程中执行
     *
     * @param bindName        注册主任务时的名称
     * @param asyncTaskStack  主任务在哪一类线程运行，目前只有两类线程，即UI线程以及非UI线程（BaseAbilitySlice.UI_STACK 以及 BaseAbilitySlice.OTHER_STACK）
     * @param beforeTaskStack 主任务前置监听器运行在哪一类线程，目前只有两类线程，即UI线程以及非UI线程（BaseAbilitySlice.UI_STACK 以及 BaseAbilitySlice.OTHER_STACK）
     * @param afterTaskStack  主任务完成时，回调任务运行在哪一类线程，目前只有两类线程，即UI线程以及非UI线程（BaseAbilitySlice.UI_STACK 以及 BaseAbilitySlice.OTHER_STACK）
     */
    protected void runTask(String bindName, int asyncTaskStack, int beforeTaskStack, int afterTaskStack) {
        AsyncTaskUtil.runAsync(this, bindName, beforeTaskStack, asyncTaskStack, afterTaskStack);
    }

    /**
     * 使用此方法，启动本地ServiceAbility
     *
     * @param serviceClass 本地Service的类对象
     */
    protected void startServiceAbility(Class<? extends Ability> serviceClass) {
        String bundleName = getBundleName();
        Intent.OperationBuilder operation = new Intent.OperationBuilder()
                .withDeviceId("")
                .withBundleName(bundleName)
                .withAbilityName(serviceClass);
        Intent intent = new Intent();
        intent.setOperation(operation.build());
        startAbility(intent);
    }

    /**
     * 调用此方法启动远程ServiceAbility
     *
     * @param deviceId    远程设备Id
     * @param bundlerName 远程App的包名
     * @param className   远程ServiceAbility的全类名，需要去除掉app的包名
     *                    例如：app包名 com.simple.relaxed 类名 com.simple.relaxed.service.ServiceBaseAbility
     *                    则此处的className则应该填入 service.ServiceBaseAbility，bundlerName应该填入 com.simple.relaxed
     */
    protected void startServiceAbility(String deviceId, String bundlerName, String className) {
        Intent.OperationBuilder operationBuilder = new Intent.OperationBuilder()
                .withDeviceId(deviceId)
                .withBundleName(bundlerName)
                .withAbilityName(className);
        Intent intent = new Intent();
        intent.setOperation(operationBuilder.build());
        startAbility(intent);
    }

    /**
     * 调用此方法，连接指定的ServiceAbility
     *
     * @param serviceAbility     指定ServiceAbility的类对象
     * @param iAbilityConnection 连接上时与断开时，会发生的回调对象
     */
    protected void connectService(Class<? extends Ability> serviceAbility, IAbilityConnection iAbilityConnection) {
        Intent.OperationBuilder operationBuilder = new Intent.OperationBuilder()
                .withDeviceId("")
                .withBundleName(getBundleName())
                .withAbilityName(serviceAbility);
        Intent intent = new Intent();
        intent.setOperation(operationBuilder.build());
        connectAbility(intent, iAbilityConnection);
    }

    /**
     * 调用此方法，连接远程的ServiceAbility
     *
     * @param operation          连接信息携带者
     * @param iAbilityConnection 连接成功与断开连接时的回调对象
     */
    protected void connectService(Operation operation, IAbilityConnection iAbilityConnection) {
        Intent intent = new Intent();
        intent.setOperation(operation);
        connectAbility(intent, iAbilityConnection);
    }

    /**
     * 调用此方法创建远程连接信息携带者
     *
     * @param deviceId    远程设备id
     * @param bundlerName 远程设备上，被连接的app的包名
     * @param abilityName 远程设备上被连接的app的服务名它和bundlerName拼接成了被调用服务的完整类名
     * @return 远程连接信息携带者
     */
    protected Operation createOperation(String deviceId, String bundlerName, String abilityName) {
        Intent.OperationBuilder operationBuilder = new Intent.OperationBuilder()
                .withDeviceId(deviceId)
                .withBundleName(bundlerName)
                .withAbilityName(abilityName);
        return operationBuilder.build();
    }

    @Override
    public void terminateAbility(int requestCode) {
        super.terminateAbility(requestCode);
        RegisterSlice registerSlice = aClass.getDeclaredAnnotation(RegisterSlice.class);
        if (registerSlice != null) {
            SliceManager.getInstance().unregister(registerSlice.bindName());
        }
    }

    /**
     * 寻找此类中，被OnKeyUp注解标记的方法，并且注入到 keyUpMethods中管理
     */
    private void initMethodToKeyUp() {
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            OnKeyUp onKeyUp = method.getDeclaredAnnotation(OnKeyUp.class);
            if (onKeyUp == null) {
                continue;
            }
            int keyCode = onKeyUp.keyCode();
            keyUpMethods.put(keyCode, method);
        }
    }

    /**
     * 寻找被AddRoute注解标记的Method方法，并且触发它
     */
    private void initAddRoute() {
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            AddRoute addRoute = method.getDeclaredAnnotation(AddRoute.class);
            if (addRoute == null) {
                continue;
            }
            method.setAccessible(true);
            try {
                method.invoke(this);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            break;
        }
    }
}
